iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 6
0
自我挑戰組

Android Architecture 及 Unit Test系列 第 6

[Day 6] Android Architecture Components:ViewModel

  • 分享至 

  • xImage
  •  

有時候在螢幕旋轉時或是 App 修改配置的時候, Activity 的生命週期會從 onDestroy -> onCreate ,這時顯示在 UI 上的一些資料就會因為 Activity 被重新建立而消失。

由於初始化了 Activity ,包括 Activity 中的 ViewModel 等也一併被重新建立,因為新的 ViewModel 沒有資料,理所當然的 UI 上也沒有任何資料顯示了。

另一個問題是有時候 View 層需要需要執行一些非同步的呼叫,並且必須確保在 App 配置改變時被系統註銷,同時又要確保 Activity 等被重新建立時這些 Callback 能夠恢復,這會需要花費很大的心力維護。

Google 為此推出了一個好用的工具:ViewModel

實現 ViewModel

由於有著 Lifecycle-Aware Components 的幫助, ViewModel 可以在 App配置改變時自動保存 instance ,以便其資料可以在接下來的 Activity 或是 Fragment 使用。

要使用 ViewModel 首先必須在 project gradle 加入以下指令:

allprojects {
    repositories {
        google()
        jcenter()
    }
}

然後再 app gradle 加入以下指令:

dependencies {
    def lifecycle_version = "2.1.0"
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
}

接著修改 TasksViewModel 及 TasksActivity :

class TasksViewModel : ViewModel() {
    ......
}

class TasksActivity : AppCompatActivity() {

    ......

    private lateinit var viewModel: TaskViewModel

    ......

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel = ViewModelProviders.of(this).get(TaskViewModel::class.java)

        ......
    }
    ......
}

這樣,TasksViewModel 的生命週期就會限制在 TasksActivity 的生命週期內,接下來再看看 ViewModel 的生命週期

可以看到 ViewModel 在 Activity 第一次時建立也一起建立,在螢幕旋轉時依舊存在,直到 Activity finish() 並且 destroy 時才一起消滅。

ViewModel 也提供一個 AndroidViewModel 給開發者用於需要在 ViewModel 內使用 context 的情境, AndroidViewModel 的 constructor 必須注入 application context 。

ViewModelProvider.Factory

有時候我們會需要往 ViewModel 內注入 entity ,例如前文中示範的 ViewModel 在 constructor 內注入 TaskRepository ,這時使用 ViewModelProviders.of(this) 是無法辦到的,那麼我們就必須要自己實現創建 ViewModel 的工廠類 ViewModelProvider.Factory

首先要在 TasksViewModel 加入 constructor:

class TasksViewModel(private val repository: TasksRepository) : ViewModel() {
    ......
}

接著建立 TodoViewModelFactory

@Suppress("UNCHECKED_CAST")
class TodoViewModelFactory constructor(
    private val tasksRepository: TasksRepository
) : ViewModelProvider.NewInstanceFactory() {
    override fun <T : ViewModel> create(modelClass: Class<T>) =
            with(modelClass) {
                when {
                    isAssignableFrom(TasksViewModel::class.java) ->
                        TasksViewModel(tasksRepository)
                    else ->
                        throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
                }
            } as T
}

在 Factory 類的 constructor 注入 TasksRepository ,並且 override create 方法返回需要的 ViewModel。

最後修改在 Activity 內的調用方法:

class TasksActivity : AppCompatActivity() {

    private val repository by lazy { TasksRepository() }
    
    private val viewModelFactory by lazy { TodoViewModelFactory(repository) }
    
    private lateinit var viewModel: TaskViewModel

    ......

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel = ViewModelProviders
            .of(this, viewModelFactory)
            .get(TaskViewModel::class.java)

        ......
    }
    ......
}

如此就完成簡單的 ViewModelFactory ,當然在實務上對 Factory 還有一些封裝,這些等到後面幾天再一一介紹。


上一篇
[Day 5] Kotlin Coroutines:Part 3 Real Work
下一篇
[Day 7] Android Architecture Components:LiveData
系列文
Android Architecture 及 Unit Test30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言